home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / ediff / ediff-mult.el.z / ediff-mult.el
Encoding:
Text File  |  1998-05-21  |  73.1 KB  |  2,025 lines

  1. ;;; ediff-mult.el --- support for multi-file/multi-buffer processing in Ediff
  2.  
  3. ;; Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Michael Kifer <kifer@cs.sunysb.edu>
  6.  
  7. ;; This file is part of GNU Emacs.
  8.  
  9. ;; GNU Emacs is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation; either version 2, or (at your option)
  12. ;; any later version.
  13.  
  14. ;; GNU Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ;; GNU General Public License for more details.
  18.  
  19. ;; You should have received a copy of the GNU General Public License
  20. ;; along with GNU Emacs; see the file COPYING.  If not, write to the
  21. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  22. ;; Boston, MA 02111-1307, USA.
  23.  
  24. ;;; Commentary:
  25.  
  26. ;; Users are encouraged to add functionality to this file.
  27. ;; The present file contains all the infrastructure needed for that.
  28. ;;
  29. ;; Generally, to to implement a new multisession capability within Ediff,
  30. ;; you need to tell it 
  31. ;;
  32. ;;    1. How to display the session group buffer.
  33. ;;       This function must indicate which Ediff sessions are active (+) and
  34. ;;       which are finished (-).
  35. ;;       See ediff-redraw-directory-group-buffer for an example.
  36. ;;       In all likelihood, ediff-redraw-directory-group-buffer can be used
  37. ;;       directly or after a small modification.
  38. ;;    2. What action to take when the user clicks button 2 or types v,e, or
  39. ;;       RET.  See ediff-filegroup-action.
  40. ;;    3. Provide a list of pairs or triples of file names (or buffers,
  41. ;;       depending on the particular Ediff operation you want to invoke)
  42. ;;       in the following format:
  43. ;;          (descriptor (obj1 obj2 obj3) (...) ...)
  44. ;;       Actually, the format of this list is pretty much up to the
  45. ;;       developer. The only thing is that it must be a list of lists,
  46. ;;       and the first list must describe the meta session, and subsequent
  47. ;;       elements must describe individual sessions.
  48. ;;       This descriptor must be a list of two, three, or four elements (nil
  49. ;;       or string). The function ediff-redraw-registry-buffer displays the
  50. ;;       second through last of these in the registry buffer. 
  51. ;;       Also, keep in mind that the function ediff-prepare-meta-buffer
  52. ;;       (which see) prepends the session group buffer to the descriptor and
  53. ;;       nil in front of each subsequent list (i.e., the above list 
  54. ;;       will become
  55. ;;              ((meta-buf descriptor) (nil obj1 obj2 obj3) (nil ...) ...)
  56. ;;       Ediff expects that your function (in 2 above) will arrange to
  57. ;;       replace this prepended nil (via setcar) with the actual ediff
  58. ;;       control buffer associated with an appropriate Ediff session.
  59. ;;       This is arranged through internal startup hooks that can be passed
  60. ;;       to any of Ediff major entries (such as ediff-files, epatch, etc.).
  61. ;;       See how this is done in ediff-filegroup-action.
  62. ;;
  63. ;;       Session descriptions are of the form (obj1 obj2 obj3), which
  64. ;;       describe objects relevant to the session. Usually they are names of
  65. ;;       files, but sometimes they may be other things. For instance, obj3 is
  66. ;;       nil for jobs that involve only two files. For patch jobs, obj2 and
  67. ;;       obj3 are markers that specify the patch corresponding to the file
  68. ;;       (whose name is obj1).
  69. ;;    4. Write a function that makes a call to ediff-prepare-meta-buffer
  70. ;;       passing all this info. 
  71. ;;       You may be able to use ediff-directories-internal as a template.
  72. ;;    5. If you intend to add several related pieces of functionality,
  73. ;;       you may want to keep the function in 4 as an internal version
  74. ;;       and then write several top-level interactive functions that call it
  75. ;;       with different parameters.
  76. ;;       See how ediff-directories, ediff-merge-directories, and
  77. ;;       ediff-merge-directories-with-ancestor all use
  78. ;;       ediff-directories-internal. 
  79. ;;
  80. ;; A useful addition here could be session groups selected by patterns
  81. ;; (which are different in each directory). For instance, one may want to
  82. ;; compare files of the form abc{something}.c to files old{something}.d
  83. ;; which may be in the same or different directories. Or, one may want to
  84. ;; compare all files of the form {something} to files of the form {something}~.
  85. ;;
  86. ;; Implementing this requires writing an collating function, which should pair
  87. ;; up appropriate files. It will also require a generalization of the functions
  88. ;; that do the layout of the meta- and differences buffers and of
  89. ;; ediff-filegroup-action.
  90.  
  91. ;;; Code:
  92.  
  93. (provide 'ediff-mult)
  94.  
  95. (defgroup ediff-mult nil
  96.   "Multi-file and multi-buffer processing in Ediff"
  97.   :prefix "ediff-"
  98.   :group 'ediff)
  99.  
  100.  
  101. ;; compiler pacifier
  102. (eval-when-compile
  103.   (let ((load-path (cons (expand-file-name ".") load-path)))
  104.     (or (featurep 'ediff-init)
  105.     (load "ediff-init.el" nil nil 'nosuffix))
  106.     (or (featurep 'ediff-util)
  107.     (load "ediff-util.el" nil nil 'nosuffix))
  108.     ))
  109. ;; end pacifier
  110.  
  111. (require 'ediff-init)
  112. (require 'ediff-util)
  113.  
  114. ;; meta-buffer
  115. (ediff-defvar-local ediff-meta-buffer nil "")
  116. (ediff-defvar-local ediff-parent-meta-buffer nil "")
  117. ;; the registry buffer
  118. (defvar ediff-registry-buffer nil)
  119.  
  120. (defconst ediff-meta-buffer-message "This is an Ediff Session Group Panel: %s
  121.  
  122. Useful commands:
  123.      button2, v, or RET over session record:   start that Ediff session
  124.      M:\tin sessions invoked from here, brings back this group panel
  125.      R:\tdisplay the registry of active Ediff sessions
  126.      h:\tmark session for hiding (toggle)
  127.      x:\thide marked sessions; with prefix arg: unhide
  128.      m:\tmark session for a non-hiding operation (toggle)
  129.  n,SPC:\tnext session
  130.  p,DEL:\tprevious session
  131.      E:\tbrowse Ediff on-line manual
  132.      T:\ttoggle truncation of long file names
  133.      q:\tquit this session group
  134. ")
  135.  
  136. (ediff-defvar-local ediff-meta-buffer-map nil
  137.   "The keymap for the meta buffer.")
  138. (defvar ediff-dir-diffs-buffer-map (make-sparse-keymap)
  139.   "The keymap to be installed in the buffer showing differences between
  140. directories.")
  141.  
  142. ;; Variable specifying the action to take when the use invokes ediff in the
  143. ;; meta buffer. This is usually ediff-registry-action or ediff-filegroup-action
  144. (ediff-defvar-local ediff-meta-action-function nil "")
  145. ;; Tells ediff-update-meta-buffer how to redraw it
  146. (ediff-defvar-local ediff-meta-redraw-function nil "")
  147. ;; Tells ediff-filegroup-action and similar procedures how to invoke Ediff for
  148. ;; the sessions in a given session group
  149. (ediff-defvar-local ediff-session-action-function nil "")
  150.  
  151. (ediff-defvar-local ediff-metajob-name nil "")
  152.  
  153. ;; buffer used to collect custom diffs from individual sessions in the group
  154. (ediff-defvar-local ediff-meta-diff-buffer nil "")
  155.  
  156. ;; history var to use for filtering groups
  157. (defvar ediff-filtering-regexp-history nil "")
  158.  
  159. ;; This has the form ((meta-buf regexp dir1 dir2 dir3 merge-auto-store-dir)
  160. ;; (ctl-buf session-status (file1 . eq-status) (file2 . eq-status) (file3
  161. ;; . eq-status)) (ctl-buf session-status (file1 . eq-status) (file2
  162. ;; . eq-status)) ...)
  163. ;; If ctl-buf is nil, the file-pair hasn't processed yet. If it is
  164. ;; killed-buffer object, the file pair has been processed. If it is a live
  165. ;; buffer, this means ediff is still working on the pair.
  166. ;; Eq-status of a file is t if the file equals some other file in the same
  167. ;; group.
  168. (ediff-defvar-local ediff-meta-list nil "")
  169.  
  170. (ediff-defvar-local ediff-meta-session-number nil "")
  171.  
  172.  
  173. ;; the difference list between directories in a directory session group
  174. (ediff-defvar-local ediff-dir-difference-list nil "")
  175. (ediff-defvar-local ediff-dir-diffs-buffer nil "")
  176.  
  177. ;; The registry of Ediff sessions. A list of control buffers.
  178. (defvar ediff-session-registry nil)
  179.  
  180. (defcustom ediff-meta-truncate-filenames t
  181.   "*If non-nil, truncate long file names in the session group buffers.
  182. This can be toggled with `ediff-toggle-filename-truncation'."
  183.   :type 'hook
  184.   :group 'ediff-mult)
  185. (defcustom ediff-registry-setup-hook nil
  186.   "*Hooks run just after the registry control panel is set up."
  187.   :type 'hook
  188.   :group 'ediff-mult)
  189. (defcustom ediff-session-group-setup-hook nil
  190.   "*Hooks run just after a meta-buffer controlling a session group, such as
  191. ediff-directories, is run."
  192.   :type 'hook
  193.   :group 'ediff-mult)
  194. (defcustom ediff-quit-session-group-hook nil
  195.   "*Hooks run just before exiting a session group."
  196.   :type 'hook
  197.   :group 'ediff-mult)
  198. (defcustom ediff-show-registry-hook nil
  199.   "*Hooks run just after the registry buffer is shown."
  200.   :type 'hook
  201.   :group 'ediff-mult)
  202. (defcustom ediff-show-session-group-hook nil
  203.   "*Hooks run just after a session group buffer is shown."
  204.   :type 'hook
  205.   :group 'ediff-mult)
  206. (defcustom ediff-meta-buffer-keymap-setup-hook nil
  207.   "*Hooks run just after setting up the ediff-meta-buffer-map.
  208. This keymap controls key bindings in the meta buffer and is a local variable.
  209. This means that you can set different bindings for different kinds of meta
  210. buffers."
  211.   :type 'hook
  212.   :group 'ediff-mult)
  213.  
  214. ;; buffer holding the multi-file patch. local to the meta buffer
  215. (ediff-defvar-local ediff-meta-patchbufer nil "")
  216.  
  217. ;;; API for ediff-meta-list
  218.  
  219. ;; group buffer/regexp
  220. (defsubst ediff-get-group-buffer (meta-list)
  221.   (nth 0 (car meta-list)))
  222.  
  223. (defsubst ediff-get-group-regexp (meta-list)
  224.   (nth 1 (car meta-list)))
  225. ;; group objects
  226. (defsubst ediff-get-group-objA (meta-list)
  227.   (nth 2 (car meta-list)))
  228. (defsubst ediff-get-group-objB (meta-list)
  229.   (nth 3 (car meta-list)))
  230. (defsubst ediff-get-group-objC (meta-list)
  231.   (nth 4 (car meta-list)))
  232. (defsubst ediff-get-group-merge-autostore-dir (meta-list)
  233.   (nth 5 (car meta-list)))
  234.  
  235. ;; session buffer
  236. (defsubst ediff-get-session-buffer (elt)
  237.   (nth 0 elt))
  238. (defsubst ediff-get-session-status (elt)
  239.   (nth 1 elt))
  240. (defsubst ediff-set-session-status (session-info new-status)
  241.   (setcar (cdr session-info) new-status))
  242. ;; session objects
  243. (defsubst ediff-get-session-objA (elt)
  244.   (nth 2 elt))
  245. (defsubst ediff-get-session-objB (elt)
  246.   (nth 3 elt))
  247. (defsubst ediff-get-session-objC (elt)
  248.   (nth 4 elt))
  249. (defsubst ediff-get-session-objA-name (elt)
  250.   (car (nth 2 elt)))
  251. (defsubst ediff-get-session-objB-name (elt)
  252.   (car (nth 3 elt)))
  253. (defsubst ediff-get-session-objC-name (elt)
  254.   (car (nth 4 elt)))
  255. ;; equality indicators
  256. (defsubst ediff-get-file-eqstatus (elt)
  257.   (nth 1 elt))
  258. (defsubst ediff-set-file-eqstatus (elt value)
  259.   (setcar (cdr elt) value))
  260.  
  261. ;; The activity marker is either or + (active session, i.e., ediff is currently
  262. ;; run in it), or - (finished session, i.e., we've ran ediff in it and then
  263. ;; exited). Return nil, if session is neither active nor finished
  264. (defun ediff-get-session-activity-marker (session)
  265.   (let ((session-buf (ediff-get-session-buffer session)))
  266.     (cond ((null session-buf) nil) ; virgin session
  267.       ((ediff-buffer-live-p session-buf) ?+) ;active session
  268.       (t ?-))))
  269.  
  270. ;; checks if the session is a meta session
  271. (defun ediff-meta-session-p (session-info)
  272.   (and (stringp (ediff-get-session-objA-name session-info))
  273.        (file-directory-p (ediff-get-session-objA-name session-info)) 
  274.        (stringp (ediff-get-session-objB-name session-info))
  275.        (file-directory-p (ediff-get-session-objB-name session-info))
  276.        (if (stringp (ediff-get-session-objC-name session-info))
  277.        (file-directory-p (ediff-get-session-objC-name session-info)) t)))
  278.  
  279. ;; set up the keymap in the meta buffer
  280. (defun ediff-setup-meta-map()
  281.   (setq ediff-meta-buffer-map (make-sparse-keymap))
  282.   (suppress-keymap ediff-meta-buffer-map)
  283.   (define-key ediff-meta-buffer-map "q" 'ediff-quit-meta-buffer)
  284.   (define-key ediff-meta-buffer-map "T" 'ediff-toggle-filename-truncation)
  285.   (define-key ediff-meta-buffer-map "R" 'ediff-show-registry)
  286.   (define-key ediff-meta-buffer-map "E" 'ediff-documentation)
  287.   (define-key ediff-meta-buffer-map "v" ediff-meta-action-function)
  288.   (define-key ediff-meta-buffer-map "\C-m" ediff-meta-action-function)
  289.   (define-key ediff-meta-buffer-map  " "  'ediff-next-meta-item)
  290.   (define-key ediff-meta-buffer-map  "n"  'ediff-next-meta-item)
  291.   (define-key ediff-meta-buffer-map  "\C-?"  'ediff-previous-meta-item)
  292.   (define-key ediff-meta-buffer-map  "p"  'ediff-previous-meta-item)
  293.   (define-key ediff-meta-buffer-map  [delete]  'ediff-previous-meta-item)
  294.   (define-key ediff-meta-buffer-map  [backspace]  'ediff-previous-meta-item)
  295.   (or (ediff-one-filegroup-metajob)
  296.       (define-key ediff-meta-buffer-map "=" 'ediff-meta-mark-equal-files))
  297.   (if ediff-no-emacs-help-in-control-buffer
  298.       (define-key ediff-meta-buffer-map  "\C-h"  'ediff-previous-meta-item))
  299.   (if ediff-emacs-p
  300.       (define-key ediff-meta-buffer-map [mouse-2] ediff-meta-action-function)
  301.     (define-key ediff-meta-buffer-map [button2] ediff-meta-action-function))
  302.  
  303.   (use-local-map ediff-meta-buffer-map)
  304.   ;; modify ediff-meta-buffer-map here
  305.   (run-hooks 'ediff-meta-buffer-keymap-setup-hook))
  306.  
  307. (defun ediff-meta-mode ()
  308.   "This mode controls all operations on Ediff session groups.
  309. It is entered through one of the following commands:
  310.     `ediff-directories'
  311.     `edirs'
  312.     `ediff-directories3'
  313.     `edirs3'
  314.     `ediff-merge-directories'
  315.     `edirs-merge'
  316.     `ediff-merge-directories-with-ancestor'
  317.     `edirs-merge-with-ancestor'
  318.     `ediff-directory-revisions'
  319.     `edir-revisions'
  320.     `ediff-merge-directory-revisions'
  321.     `edir-merge-revisions'
  322.     `ediff-merge-directory-revisions-with-ancestor'
  323.     `edir-merge-revisions-with-ancestor'
  324.  
  325. Commands:
  326. \\{ediff-meta-buffer-map}"
  327.   (kill-all-local-variables)
  328.   (setq major-mode 'ediff-meta-mode)
  329.   (setq mode-name "MetaEdiff"))
  330.  
  331.  
  332. ;; the keymap for the buffer showing directory differences
  333. (suppress-keymap ediff-dir-diffs-buffer-map)
  334. (define-key ediff-dir-diffs-buffer-map "q" 'ediff-bury-dir-diffs-buffer)
  335. (define-key ediff-dir-diffs-buffer-map " " 'next-line)
  336. (define-key ediff-dir-diffs-buffer-map "n" 'next-line)
  337. (define-key ediff-dir-diffs-buffer-map "\C-?" 'previous-line)
  338. (define-key ediff-dir-diffs-buffer-map "p" 'previous-line)
  339. (define-key ediff-dir-diffs-buffer-map [delete] 'previous-line)
  340. (define-key ediff-dir-diffs-buffer-map [backspace] 'previous-line)
  341.  
  342. (defun ediff-next-meta-item (count)
  343.   "Move to the next item in Ediff registry or session group buffer.
  344. Moves in circular fashion. With numeric prefix arg, skip this many items."
  345.   (interactive "p")
  346.   (or count (setq count 1))
  347.   (let (overl)
  348.     (while (< 0 count)
  349.       (setq count (1- count))
  350.       (ediff-next-meta-item1)
  351.       (setq overl (ediff-get-meta-overlay-at-pos (point)))
  352.       ;; skip invisible ones
  353.       (while (and overl (ediff-overlay-get overl 'invisible))
  354.     (ediff-next-meta-item1)
  355.     (setq overl (ediff-get-meta-overlay-at-pos (point)))))))
  356.  
  357. ;; Move to the next meta item
  358. (defun ediff-next-meta-item1 ()
  359.   (let (pos)
  360.     (setq pos (ediff-next-meta-overlay-start (point)))
  361.     (if pos (goto-char pos))
  362.     (if (eq ediff-metajob-name 'ediff-registry)
  363.     (if (and (ediff-get-meta-info (current-buffer) pos 'noerror)
  364.          (search-forward "*Ediff" nil t))
  365.         (skip-chars-backward "a-zA-Z*"))
  366.       (if (> (skip-chars-forward "-+?H* \t0-9") 0)
  367.       (backward-char 1)))))
  368.  
  369.  
  370. (defun ediff-previous-meta-item (count)
  371.   "Move to the previous item in Ediff registry or session group buffer.
  372. Moves in circular fashion. With numeric prefix arg, skip this many items."
  373.   (interactive "p")
  374.   (or count (setq count 1))
  375.   (let (overl)
  376.     (while (< 0 count)
  377.       (setq count (1- count))
  378.       (ediff-previous-meta-item1)
  379.       (setq overl (ediff-get-meta-overlay-at-pos (point)))
  380.       ;; skip invisible ones
  381.       (while (and overl (ediff-overlay-get overl 'invisible))
  382.     (ediff-previous-meta-item1)
  383.     (setq overl (ediff-get-meta-overlay-at-pos (point)))))))
  384.  
  385. (defun ediff-previous-meta-item1 ()
  386.   (let (pos)
  387.     (setq pos (ediff-previous-meta-overlay-start (point)))
  388. ;;;    ;; skip deleted
  389. ;;;    (while (ediff-get-session-status
  390. ;;;        (ediff-get-meta-info (current-buffer) pos 'noerror))
  391. ;;;      (setq pos (ediff-previous-meta-overlay-start pos)))
  392.     
  393.     (if pos (goto-char pos))
  394.     (if (eq ediff-metajob-name 'ediff-registry)
  395.     (if (and (ediff-get-meta-info (current-buffer) pos 'noerror)
  396.          (search-forward "*Ediff" nil t))
  397.         (skip-chars-backward "a-zA-Z*"))
  398.       (if (> (skip-chars-forward "-+?H* \t0-9") 0)
  399.       (backward-char 1)))
  400.     ))
  401.  
  402. (defsubst ediff-add-slash-if-directory (dir file)
  403.   (if (file-directory-p (concat dir file))
  404.       (file-name-as-directory file)
  405.     file))
  406.  
  407. (defun ediff-toggle-filename-truncation ()
  408.   "Toggle truncation of long file names in session group buffers.
  409. Set `ediff-meta-truncate-filenames' variable if you want to change the default
  410. behavior."
  411.   (interactive)
  412.   (setq ediff-meta-truncate-filenames (not ediff-meta-truncate-filenames))
  413.   (ediff-update-meta-buffer (current-buffer) 'must-redraw))
  414.  
  415.  
  416. ;; DIR1, DIR2, DIR3 are directories. DIR3 can be nil.
  417. ;; OUTPUT-DIR is a directory for auto-storing the results of merge jobs.
  418. ;;          Can be nil.
  419. ;; REGEXP is a regexp used to filter out files in the directories.
  420. ;; If a file is a directory in dir1 but not dir2 (or vice versa), it is not
  421. ;; included in the intersection. However, a regular file that is a dir in dir3
  422. ;; is included, since dir3 files are supposed to be ancestors for merging.
  423. ;; Returns a list of the form:
  424. ;;    ((dir1 dir2 dir3) (f1 f2 f3) (f1 f2 f3) ...)
  425. ;; dir3, f3 can be nil if intersecting only 2 directories.
  426. ;; If COMPARISON-FUNC is given, use it. Otherwise, use string=
  427. ;; DIFF-VAR contains the name of the variable in which to return the
  428. ;; difference list (which represents the differences among the contents of
  429. ;; directories). The diff list is of the form:
  430. ;;    ((dir1 dir2 dir3) (file . num) (file . num)...)
  431. ;; where num encodes the set of dirs where the file is found:
  432. ;; 2 - only dir1; 3 - only dir2; 5 - only dir3; 6 - dir1&2; 10 - dir1&3; etc.
  433. (defun ediff-intersect-directories (jobname
  434.                     diff-var regexp dir1 dir2
  435.                     &optional
  436.                     dir3 merge-autostore-dir comparison-func)
  437.   (setq comparison-func (or comparison-func 'string=))
  438.   (let (lis1 lis2 lis3 common auxdir1 auxdir2 auxdir3 difflist)
  439.  
  440.     (setq auxdir1    (file-name-as-directory dir1)
  441.       lis1        (directory-files auxdir1 nil regexp)
  442.       lis1         (delete "."  lis1)
  443.       lis1         (delete ".." lis1)
  444.       lis1         (mapcar 
  445.              (function 
  446.               (lambda (elt)
  447.                   (ediff-add-slash-if-directory auxdir1 elt)))
  448.              lis1)
  449.       auxdir2    (file-name-as-directory dir2)
  450.       lis2        (mapcar 
  451.              (function
  452.               (lambda (elt)
  453.                   (ediff-add-slash-if-directory auxdir2 elt)))
  454.              (directory-files auxdir2 nil regexp)))
  455.  
  456.     (if (stringp dir3)
  457.     (setq auxdir3    (file-name-as-directory dir3)
  458.           lis3    (mapcar 
  459.              (function
  460.               (lambda (elt)
  461.                   (ediff-add-slash-if-directory auxdir3 elt)))
  462.              (directory-files auxdir3 nil regexp))))
  463.  
  464.     (if (stringp merge-autostore-dir)
  465.     (setq merge-autostore-dir
  466.           (file-name-as-directory merge-autostore-dir)))
  467.     (setq common (ediff-intersection lis1 lis2 comparison-func))
  468.  
  469.     ;; In merge with ancestor jobs, we don't intersect with lis3.
  470.     ;; If there is no ancestor, we'll offer to merge without the ancestor.
  471.     ;; So, we intersect with lis3 only when we are doing 3-way file comparison
  472.     (if (and lis3 (ediff-comparison-metajob3 jobname))
  473.     (setq common (ediff-intersection common lis3 comparison-func)))
  474.  
  475.     ;; copying is needed because sort sorts via side effects
  476.     (setq common (sort (ediff-copy-list common) 'string-lessp))
  477.  
  478.     ;; compute difference list
  479.     (setq difflist (ediff-set-difference
  480.             (ediff-union (ediff-union lis1 lis2 comparison-func)
  481.                  lis3
  482.                  comparison-func)
  483.             common
  484.             comparison-func)
  485.       difflist (delete "."  difflist)
  486.       ;; copying is needed because sort sorts via side effects
  487.       difflist (sort (ediff-copy-list (delete ".." difflist))
  488.              'string-lessp))
  489.  
  490.     (setq difflist (mapcar (function (lambda (elt) (cons elt 1))) difflist))
  491.  
  492.     ;; check for files belonging to lis1/2/3
  493.     (mapcar (function (lambda (elt) 
  494.             (if (member (car elt) lis1)
  495.                 (setcdr elt (* (cdr elt) 2)))
  496.             (if (member (car elt) lis2)
  497.                 (setcdr elt (* (cdr elt) 3)))
  498.             (if (member (car elt) lis3)
  499.                 (setcdr elt (* (cdr elt) 5)))
  500.             ))
  501.         difflist)
  502.     (setq difflist (cons (list regexp auxdir1 auxdir2 auxdir3) difflist))
  503.     
  504.     ;; return the difference list back to the calling function
  505.     (set diff-var difflist)
  506.  
  507.     ;; return result
  508.     (cons (list regexp auxdir1 auxdir2 auxdir3 merge-autostore-dir)
  509.       (mapcar
  510.        (function
  511.         (lambda (elt) 
  512.           (list (concat auxdir1 elt)
  513.             (concat auxdir2 elt)
  514.             (if lis3
  515.             (progn
  516.               ;; The following is done because:
  517.               ;;   In merging with ancestor, we don't intersect
  518.               ;;   with lis3. So, it is possible that elt is a
  519.               ;;   file in auxdir1/2 but a directory in auxdir3
  520.               ;;   Or elt may not exist in auxdir3 at all.
  521.               ;;   In the first case, we add a slash at the end.
  522.               ;;   In the second case, we insert nil.
  523.               (setq elt (ediff-add-slash-if-directory auxdir3 elt))
  524.               (if (file-exists-p (concat auxdir3 elt))
  525.                   (concat auxdir3 elt)))))))
  526.        common))
  527.     ))
  528.  
  529. ;; find directory files that are under revision.  Include subdirectories, since
  530. ;; we may visit them recursively.  DIR1 is the directory to inspect.
  531. ;; MERGE-AUTOSTORE-DIR is the directory where to auto-store the results of
  532. ;; merges. Can be nil.
  533. (defun ediff-get-directory-files-under-revision (jobname
  534.                          regexp dir1
  535.                          &optional merge-autostore-dir)
  536.   (let (lis1 elt common auxdir1)
  537.     (setq auxdir1 (file-name-as-directory dir1)
  538.       lis1      (directory-files auxdir1 nil regexp))
  539.  
  540.     (if (stringp merge-autostore-dir)
  541.     (setq merge-autostore-dir
  542.           (file-name-as-directory merge-autostore-dir)))
  543.  
  544.     (while lis1
  545.       (setq elt  (car lis1)
  546.         lis1 (cdr lis1))
  547.       ;; take files under revision control
  548.       (cond ((file-directory-p (concat auxdir1 elt))
  549.          (setq common
  550.            (cons (ediff-add-slash-if-directory auxdir1 elt) common)))
  551.         ((file-exists-p (concat auxdir1 elt ",v"))
  552.          (setq common (cons elt common)))
  553.         ((file-exists-p (concat auxdir1 "RCS/" elt ",v"))
  554.          (setq common (cons elt common)))
  555.         ) ; cond
  556.       ) ; while
  557.  
  558.     (setq common (delete "./"  common)
  559.       common (delete "../" common)
  560.       common (delete "RCS" common))
  561.  
  562.     ;; copying is needed because sort sorts via side effects
  563.     (setq common (sort (ediff-copy-list common) 'string-lessp))
  564.  
  565.     ;; return result
  566.     (cons (list regexp auxdir1 nil nil merge-autostore-dir)
  567.       (mapcar (function (lambda (elt) 
  568.                   (list (concat auxdir1 elt)
  569.                     nil nil)))
  570.           common))
  571.     ))
  572.       
  573.  
  574. ;; If file groups selected by patterns will ever be implemented, this
  575. ;; comparison function might become useful.
  576. ;;;; uses external variables PAT1 PAT2 to compare str1/2
  577. ;;;; patterns must be of the form ???*???? where ??? are strings of chars
  578. ;;;; containing no *.
  579. ;;(defun ediff-pattern= (str1 str2)
  580. ;;  (let (pos11 pos12 pos21 pos22 len1 len2)
  581. ;;    (setq pos11 0
  582. ;;      len  (length epat1)
  583. ;;      pos12 len)
  584. ;;    (while (and (< pos11 len) (not (= (aref epat1 pos11) ?*)))
  585. ;;      (setq pos11 (1+ pos11)))
  586. ;;    (while (and (> pos12 0) (not (= (aref epat1 (1- pos12)) ?*)))
  587. ;;      (setq pos12 (1- pos12)))
  588. ;;
  589. ;;    (setq pos21 0
  590. ;;      len  (length epat2)
  591. ;;      pos22 len)
  592. ;;    (while (and (< pos21 len) (not (= (aref epat2 pos21) ?*)))
  593. ;;      (setq pos21 (1+ pos21)))
  594. ;;    (while (and (> pos22 0) (not (= (aref epat2 (1- pos22)) ?*)))
  595. ;;      (setq pos22 (1- pos22)))
  596. ;;
  597. ;;    (if (and (> (length str1) pos12) (>= pos12 pos11) (> pos11 -1)
  598. ;;         (> (length str2) pos22) (>= pos22 pos21) (> pos21 -1))
  599. ;;    (string= (substring str1 pos11 pos12)
  600. ;;         (substring str2 pos21 pos22)))
  601. ;;    ))
  602.  
  603.  
  604. ;; Prepare meta-buffer in accordance with the argument-function and
  605. ;; redraw-function. Must return the created  meta-buffer.
  606. (defun ediff-prepare-meta-buffer (action-func meta-list
  607.                   meta-buffer-name redraw-function
  608.                   jobname &optional startup-hooks)
  609.   (let* ((meta-buffer-name 
  610.       (ediff-unique-buffer-name meta-buffer-name "*"))
  611.      (meta-buffer (get-buffer-create meta-buffer-name)))
  612.     (ediff-with-current-buffer meta-buffer
  613.  
  614.       ;; comes first
  615.       (ediff-meta-mode)
  616.  
  617.       (setq ediff-meta-action-function action-func
  618.         ediff-meta-redraw-function redraw-function
  619.         ediff-metajob-name jobname
  620.         ediff-meta-buffer meta-buffer)
  621.  
  622.       ;; comes after ediff-meta-action-function is set
  623.       (ediff-setup-meta-map)
  624.       
  625.       (if (eq ediff-metajob-name 'ediff-registry)
  626.       (progn
  627.         (setq ediff-registry-buffer meta-buffer
  628.           ediff-meta-list meta-list)
  629.         ;; this func is used only from registry buffer, not from other
  630.         ;; meta-buffs.
  631.         (define-key
  632.           ediff-meta-buffer-map "M" 'ediff-show-meta-buff-from-registry))
  633.     ;; Initialize the meta list -- don't do this for registry.
  634.     ;;
  635.     ;; We prepend '(nil nil) to all elts of meta-list, except the first.
  636.     ;; The first nil will later be replaced by the session buffer. The
  637.     ;; second is reserved for session status.
  638.     ;;
  639.     ;; (car ediff-meta-list) gets cons'ed with the session group buffer.
  640.     ;; Also, session objects A/B/C are turned into lists of the form
  641.     ;; (obj eq-indicator). Eq-indicator is either nil or =. Initialized to
  642.     ;; nil. If later it is discovered that this file is = to some other
  643.     ;; file in the same session, eq-indicator is changed to `='.
  644.     ;; For now, the eq-indicator is used only for 2 and 3-file jobs.
  645.     (setq ediff-meta-list
  646.           (cons (cons meta-buffer (car meta-list))
  647.             (mapcar
  648.              (function
  649.               (lambda (elt)
  650.             (cons nil
  651.                   (cons nil
  652.                     ;; convert each obj to (obj nil),
  653.                     ;; where nil is the initial value
  654.                     ;; for eq-indicator -- see above
  655.                     (mapcar
  656.                      (function (lambda (obj) (list obj nil)))
  657.                      elt)))))
  658.              (cdr meta-list)))))
  659.     
  660.       (or (eq meta-buffer ediff-registry-buffer)
  661.       (setq ediff-session-registry
  662.         (cons meta-buffer ediff-session-registry)))
  663.     
  664.       ;; redraw-function uses ediff-meta-list
  665.       (funcall redraw-function ediff-meta-list)
  666.       
  667.       ;; set read-only/non-modified
  668.       (setq buffer-read-only t)
  669.       (set-buffer-modified-p nil)
  670.  
  671.       (run-hooks 'startup-hooks)
  672.  
  673.       ;; Arrange to show directory contents differences
  674.       ;; Must be after run startup-hooks, since ediff-dir-difference-list is
  675.       ;; set inside these hooks
  676.       (if (eq action-func 'ediff-filegroup-action)
  677.       (progn
  678.         ;; put meta buffer in (car ediff-dir-difference-list)
  679.         (setq ediff-dir-difference-list
  680.           (cons (cons meta-buffer (car ediff-dir-difference-list))
  681.             (cdr ediff-dir-difference-list)))
  682.  
  683.         (or (ediff-one-filegroup-metajob jobname)
  684.         (ediff-draw-dir-diffs ediff-dir-difference-list))
  685.         (define-key ediff-meta-buffer-map "h" 'ediff-mark-for-hiding)
  686.         (define-key
  687.           ediff-meta-buffer-map "x" 'ediff-hide-marked-sessions)
  688.         (define-key ediff-meta-buffer-map "m" 'ediff-mark-for-operation)
  689.         (cond ((ediff-collect-diffs-metajob jobname)
  690.            (define-key
  691.              ediff-meta-buffer-map "P" 'ediff-collect-custom-diffs))
  692.           ((ediff-patch-metajob jobname)
  693.            (define-key
  694.              ediff-meta-buffer-map "P" 'ediff-meta-show-patch)))
  695.         (define-key ediff-meta-buffer-map "u" 'ediff-up-meta-hierarchy)
  696.         (define-key ediff-meta-buffer-map "D" 'ediff-show-dir-diffs)))
  697.  
  698.       (if (eq ediff-metajob-name 'ediff-registry)
  699.       (run-hooks 'ediff-registry-setup-hook)
  700.     (run-hooks 'ediff-session-group-setup-hook))
  701.       ) ; eval in meta-buffer
  702.     meta-buffer))
  703.  
  704. ;; Insert the activity marker for session SESSION in the meta buffer at point
  705. ;; The activity marker is either SPC (untouched session), or + (active session,
  706. ;; i.e., ediff is currently run in it), or - (finished session, i.e., we've ran
  707. ;; ediff in it and then exited)
  708. (defun ediff-insert-session-activity-marker-in-meta-buffer (session)
  709.   (insert
  710.    (cond ((ediff-get-session-activity-marker session))
  711.      ;; virgin session
  712.      (t " "))))
  713.  
  714. ;; Insert session status at point. Status is either ?H (marked for hiding), or
  715. ;; ?I (hidden or invalid), or ?* (meaning marked for an operation; currently,
  716. ;; such op can only be checking for equality)), or SPC (meaning neither marked
  717. ;; nor invalid) 
  718. (defun ediff-insert-session-status-in-meta-buffer (session)
  719.   (insert
  720.    (cond ((ediff-get-session-status session)) ; session has status: ?H, ?I, ?*
  721.      ;; normal session, no marks or hidings
  722.      (t " "))))
  723.  
  724. ;; If NEW-MARKER is non-nil, use it to substitute the current activity marker
  725. ;; in the meta buffer. If nil, use SPC
  726. (defun ediff-replace-session-activity-marker-in-meta-buffer (point new-marker)
  727.   (let* ((overl (ediff-get-meta-overlay-at-pos point))
  728.      (session-info (ediff-overlay-get overl 'ediff-meta-info))
  729.      (activity-marker (ediff-get-session-activity-marker session-info))
  730.      buffer-read-only)
  731.     (or new-marker activity-marker (setq new-marker ?\ ))
  732.     (goto-char (ediff-overlay-start overl))
  733.     (if (eq (char-after (point)) new-marker)
  734.     () ; if marker shown in buffer is the same as new-marker, do nothing
  735.       (insert new-marker)
  736.       (delete-char 1)
  737.       (set-buffer-modified-p nil))))
  738.  
  739. ;; If NEW-STATUS is non-nil, use it to substitute the current status marker in
  740. ;; the meta buffer. If nil, use SPC
  741. (defun ediff-replace-session-status-in-meta-buffer (point new-status)
  742.   (let* ((overl (ediff-get-meta-overlay-at-pos point))
  743.      (session-info (ediff-overlay-get overl 'ediff-meta-info))
  744.      (status (ediff-get-session-status session-info))
  745.      buffer-read-only)
  746.     (setq new-status (or new-status status ?\ ))
  747.     (goto-char (ediff-overlay-start overl))
  748.     (forward-char 1) ; status is the second char in session record
  749.     (if (eq (char-after (point)) new-status)
  750.     () ; if marker shown in buffer is the same as new-marker, do nothing
  751.       (insert new-status)
  752.       (delete-char 1)
  753.       (set-buffer-modified-p nil))))
  754.  
  755. ;; insert all file info in meta buffer for a given session
  756. (defun ediff-insert-session-info-in-meta-buffer (session-info sessionNum)
  757.   (let ((f1 (ediff-get-session-objA session-info))
  758.     (f2 (ediff-get-session-objB session-info))
  759.     (f3 (ediff-get-session-objC session-info))
  760.     (pt (point))
  761.     (hidden (eq (ediff-get-session-status session-info) ?I)))
  762.     ;; insert activity marker, i.e., SPC, - or +
  763.     (ediff-insert-session-activity-marker-in-meta-buffer session-info)
  764.     ;; insert session status, i.e., *, H
  765.     (ediff-insert-session-status-in-meta-buffer session-info)
  766.     (insert "  Session " (int-to-string sessionNum) ":\n")
  767.     (ediff-meta-insert-file-info1 f1)
  768.     (ediff-meta-insert-file-info1 f2)
  769.     (ediff-meta-insert-file-info1 f3)
  770.     (ediff-set-meta-overlay pt (point) session-info sessionNum hidden)))
  771.  
  772.  
  773. ;; this is a setup function for ediff-directories
  774. ;; must return meta-buffer
  775. (defun ediff-redraw-directory-group-buffer (meta-list)
  776.   ;; extract directories
  777.   (let ((meta-buf (ediff-get-group-buffer meta-list))
  778.     (empty t)
  779.     (sessionNum 0)
  780.     regexp elt merge-autostore-dir
  781.     point tmp-list buffer-read-only)
  782.     (ediff-with-current-buffer meta-buf
  783.       (setq point (point))
  784.       (erase-buffer)
  785.       ;; delete phony overlays that used to represent sessions before the buff
  786.       ;; was redrawn
  787.       (if ediff-emacs-p
  788.       (mapcar 'delete-overlay (overlays-in 1 1))
  789.     (map-extents 'delete-extent))
  790.       
  791.       (insert (format ediff-meta-buffer-message
  792.               (ediff-abbrev-jobname ediff-metajob-name)))
  793.  
  794.       (setq regexp (ediff-get-group-regexp meta-list)
  795.         merge-autostore-dir
  796.         (ediff-get-group-merge-autostore-dir meta-list))
  797.       
  798.       (cond ((ediff-collect-diffs-metajob)
  799.          (insert
  800.           "     P:\tcollect custom diffs of all marked sessions\n"))
  801.         ((ediff-patch-metajob)
  802.          (insert
  803.           "     P:\tshow patch appropriately for the context (session or group)\n")))
  804.       (insert
  805.        "     u:\tshow parent session group\n")
  806.       (or (ediff-one-filegroup-metajob)
  807.       (insert
  808.        "     D:\tshow differences among directories\n"
  809.        "     =:\tmark identical files in each session\n\n"))
  810.  
  811.       (insert "\n")
  812.       (if (and (stringp regexp) (> (length regexp) 0))
  813.       (insert
  814.        (format "*** Filter-through regular expression: %s\n" regexp)))
  815.       (ediff-insert-dirs-in-meta-buffer meta-list)
  816.       (if (and ediff-autostore-merges (ediff-merge-metajob)
  817.            (stringp merge-autostore-dir))
  818.       (insert (format
  819.            "\nMerge results are automatically stored in:\n\t%s\n"
  820.            merge-autostore-dir)))
  821.       (insert "\n
  822.         Size   Last modified           Name
  823.     ----------------------------------------------
  824.  
  825. ")
  826.       
  827.       ;; discard info on directories and regexp
  828.       (setq meta-list (cdr meta-list)
  829.         tmp-list meta-list)
  830.       (while (and tmp-list empty)
  831.     (if (and (car tmp-list)
  832.          (not (eq (ediff-get-session-status (car tmp-list)) ?I)))
  833.         (setq empty nil))
  834.     (setq tmp-list (cdr tmp-list)))
  835.  
  836.       (if empty
  837.       (insert
  838.        "     ******   ******   This session group has no members\n"))
  839.       
  840.       ;; now organize file names like this:
  841.       ;;     use-mark sizeA dateA  sizeB dateB  filename
  842.       ;; make sure directories are displayed with a trailing slash.
  843.       (while meta-list
  844.     (setq elt (car meta-list)
  845.           meta-list (cdr meta-list)
  846.           sessionNum (1+ sessionNum))
  847.     (if (eq (ediff-get-session-status elt) ?I)
  848.         ()
  849.       (ediff-insert-session-info-in-meta-buffer elt sessionNum)))
  850.       (set-buffer-modified-p nil)
  851.       (goto-char point)
  852.       meta-buf)))
  853.  
  854. (defun ediff-update-markers-in-dir-meta-buffer (meta-list)
  855.   (let ((meta-buf (ediff-get-group-buffer meta-list))
  856.     session-info point overl buffer-read-only)
  857.     (ediff-with-current-buffer meta-buf
  858.       (setq point (point))
  859.       (goto-char (point-min))
  860.       (ediff-next-meta-item1)
  861.       (while (not (bobp))
  862.     (setq session-info (ediff-get-meta-info meta-buf (point) 'no-error)
  863.           overl (ediff-get-meta-overlay-at-pos (point)))
  864.     (if session-info
  865.         (progn
  866.           (cond ((eq (ediff-get-session-status session-info) ?I)
  867.              ;; Do hiding
  868.              (if overl (ediff-overlay-put overl 'invisible t)))
  869.             ((and (eq (ediff-get-session-status session-info) ?H)
  870.               overl (ediff-overlay-get overl 'invisible))
  871.              ;; Do unhiding
  872.              (ediff-overlay-put overl 'invisible nil))
  873.             (t (ediff-replace-session-activity-marker-in-meta-buffer
  874.             (point)
  875.             (ediff-get-session-activity-marker session-info))
  876.                (ediff-replace-session-status-in-meta-buffer
  877.             (point)
  878.             (ediff-get-session-status session-info))))))
  879.     (ediff-next-meta-item1) ; advance to the next item
  880.     ) ; end while
  881.       (set-buffer-modified-p nil)
  882.       (goto-char point))
  883.     meta-buf))
  884.  
  885. (defun ediff-update-session-marker-in-dir-meta-buffer (session-num)
  886.   (let (buffer-meta-overlays session-info overl buffer-read-only)
  887.     (setq overl
  888.       (if ediff-xemacs-p
  889.           (map-extents
  890.            (function
  891.         (lambda (ext maparg)
  892.           (if (and
  893.                (ediff-overlay-get ext 'ediff-meta-info)
  894.                (eq (ediff-overlay-get ext 'ediff-meta-session-number)
  895.                session-num))
  896.               ext))))
  897.         ;; Emacs doesn't have map-extents, so try harder
  898.         ;; Splice overlay lists to get all buffer overlays
  899.         (setq buffer-meta-overlays (overlay-lists)
  900.           buffer-meta-overlays (append (car buffer-meta-overlays)
  901.                            (cdr buffer-meta-overlays)))
  902.         (car
  903.          (delq nil
  904.            (mapcar
  905.             (function
  906.              (lambda (overl)
  907.                (if (and
  908.                 (ediff-overlay-get overl 'ediff-meta-info)
  909.                 (eq (ediff-overlay-get
  910.                  overl 'ediff-meta-session-number)
  911.                 session-num))
  912.                overl)))
  913.             buffer-meta-overlays)))))
  914.     (or overl
  915.     (error
  916.      "Bug in ediff-update-session-marker-in-dir-meta-buffer: no overlay with given number %S"
  917.      session-num))
  918.     (setq session-info (ediff-overlay-get overl 'ediff-meta-info))
  919.     (goto-char (ediff-overlay-start overl))
  920.     (ediff-replace-session-activity-marker-in-meta-buffer
  921.      (point)
  922.      (ediff-get-session-activity-marker session-info))
  923.     (ediff-replace-session-status-in-meta-buffer
  924.      (point)
  925.      (ediff-get-session-status session-info)))
  926.   (ediff-next-meta-item1))
  927.  
  928.  
  929.  
  930. ;; Check if this is a problematic session.
  931. ;; Return nil if not. Otherwise, return symbol representing the problem
  932. ;; At present, problematic sessions occur only in -with-ancestor comparisons
  933. ;; when the ancestor is a directory rather than a file, or when there is no
  934. ;; suitable ancestor file in the ancestor directory
  935. (defun ediff-problematic-session-p (session)
  936.   (let ((f1 (ediff-get-session-objA-name session))
  937.     (f2 (ediff-get-session-objB-name session))
  938.     (f3 (ediff-get-session-objC-name session)))
  939.     (cond ((and (stringp f1) (not (file-directory-p f1))
  940.         (stringp f2) (not (file-directory-p f2))
  941.         ;; either invalid file name or a directory
  942.         (or (not (stringp f3)) (file-directory-p f3))
  943.         (ediff-ancestor-metajob))
  944.        ;; more may be added later
  945.        'ancestor-is-dir)
  946.       (t nil))))
  947.  
  948. (defun ediff-meta-insert-file-info1 (fileinfo)
  949.   (let ((fname (car fileinfo))
  950.     (feq (ediff-get-file-eqstatus fileinfo))
  951.     (max-filename-width (if ediff-meta-truncate-filenames
  952.                 (- (window-width) 41)
  953.                   500))
  954.     file-modtime file-size)
  955.     (cond ((not (stringp fname)) (setq file-size -2)) ; file doesn't exits
  956.       ((not (ediff-file-remote-p fname))
  957.        (if (file-exists-p fname)
  958.            ;; set real size and modtime
  959.            (setq file-size (ediff-file-size fname)
  960.              file-modtime (ediff-file-modtime fname))
  961.          (setq file-size -2)))  ; file doesn't exist
  962.       ( t (setq file-size -1))) ; remote file
  963.     (if (stringp fname)
  964.     (insert
  965.      (format
  966.       "%s  %s   %-20s   %s\n"
  967.       (if feq "=" " ") ; equality indicator
  968.       (format "%10s" (cond ((= file-size -1) "--")
  969.                    ((< file-size -1) "--")
  970.                    (t file-size)))
  971.       (cond ((= file-size -1) "*remote file*")
  972.         ((< file-size -1) "*file doesn't exist*")
  973.         (t (ediff-format-date (decode-time file-modtime))))
  974.  
  975.       ;; dir names in meta lists have training slashes, so we just
  976.       ;; abbreviate the file name, if file exists
  977.       (if (and (not (stringp fname)) (< file-size -1))
  978.           "-------"        ; file doesn't exist
  979.         (ediff-truncate-string-left
  980.          (ediff-abbreviate-file-name fname)
  981.          max-filename-width)))))))
  982.  
  983. (defconst ediff-months '((1 . "Jan") (2 . "Feb") (3 . "Mar") (4 . "Apr")
  984.             (5 . "May") (6 . "Jun") (7 . "Jul") (8 . "Aug")
  985.             (9 . "Sep") (10 . "Oct") (11 . "Nov") (12 . "Dec"))
  986.   "Months' associative array.")
  987.  
  988. ;; returns 2char string
  989. (defsubst ediff-fill-leading-zero (num)
  990.   (if (< num 10)
  991.       (format "0%d" num)
  992.     (number-to-string num)))
  993.  
  994. ;; TIME is like the output of decode-time
  995. (defun ediff-format-date (time)
  996.   (format "%s %2d %4d %s:%s:%s"
  997.       (cdr (assoc (nth 4 time) ediff-months)) ; month
  998.       (nth 3 time) ; day
  999.       (nth 5 time) ; year
  1000.       (ediff-fill-leading-zero (nth 2 time)) ; hour
  1001.       (ediff-fill-leading-zero (nth 1 time)) ; min
  1002.       (ediff-fill-leading-zero (nth 0 time)) ; sec
  1003.       ))
  1004.  
  1005. ;; Draw the directories
  1006. (defun ediff-insert-dirs-in-meta-buffer (meta-list)
  1007.   (let* ((dir1 (ediff-abbreviate-file-name (ediff-get-group-objA meta-list)))
  1008.      (dir2 (ediff-get-group-objB meta-list))
  1009.      (dir2 (if (stringp dir2) (ediff-abbreviate-file-name dir2)))
  1010.      (dir3 (ediff-get-group-objC meta-list))
  1011.      (dir3 (if (stringp dir3) (ediff-abbreviate-file-name dir3))))
  1012.     (insert "*** Directory A: " dir1 "\n")
  1013.     (if dir2 (insert "*** Directory B: " dir2 "\n"))
  1014.     (if dir3 (insert "*** Directory C: " dir3 "\n"))
  1015.     (insert "\n")))
  1016.  
  1017. (defun ediff-draw-dir-diffs (diff-list)
  1018.   (if (null diff-list) (error "Lost difference info on these directories"))
  1019.   (let* ((buf-name (ediff-unique-buffer-name
  1020.             "*Ediff File Group Differences" "*"))
  1021.      (regexp (ediff-get-group-regexp diff-list))
  1022.      (dir1 (ediff-abbreviate-file-name (ediff-get-group-objA diff-list)))
  1023.      (dir2 (ediff-abbreviate-file-name (ediff-get-group-objB diff-list)))
  1024.      (dir3 (ediff-get-group-objC diff-list))
  1025.      (dir3 (if (stringp dir3) (ediff-abbreviate-file-name dir3)))
  1026.      (meta-buf (ediff-get-group-buffer diff-list))
  1027.      (underline (make-string 26 ?-))
  1028.      file code 
  1029.      buffer-read-only)
  1030.     ;; skip the directory part
  1031.     (setq diff-list (cdr diff-list))
  1032.     (setq ediff-dir-diffs-buffer (get-buffer-create buf-name))
  1033.     (ediff-with-current-buffer ediff-dir-diffs-buffer
  1034.       (use-local-map ediff-dir-diffs-buffer-map)
  1035.       (erase-buffer)
  1036.       (setq ediff-meta-buffer meta-buf)
  1037.       (insert "\t\t*** Directory Differences ***\n")
  1038.       (insert "
  1039. Useful commands:
  1040.      `q': hide this buffer
  1041.    n,SPC: next line
  1042.    p,DEL: previous line\n\n")
  1043.  
  1044.       (if (and (stringp regexp) (> (length regexp) 0))
  1045.       (insert
  1046.        (format "\n*** Filter-through regular expression: %s\n" regexp)))
  1047.       (insert "\n")
  1048.       (insert (format "\n%-27s%-26s"
  1049.               (ediff-truncate-string-left
  1050.                (ediff-abbreviate-file-name
  1051.             (file-name-as-directory dir1))
  1052.                25)
  1053.               (ediff-truncate-string-left
  1054.                (ediff-abbreviate-file-name
  1055.             (file-name-as-directory dir2))
  1056.                25)))
  1057.       (if dir3
  1058.       (insert (format " %-25s\n"
  1059.               (ediff-truncate-string-left
  1060.                (ediff-abbreviate-file-name
  1061.                 (file-name-as-directory dir3))
  1062.                25)))
  1063.     (insert "\n"))
  1064.       (insert (format "%s%s" underline underline))
  1065.       (if (stringp dir3)
  1066.       (insert (format "%s\n\n" underline))
  1067.     (insert "\n\n"))
  1068.  
  1069.       (if (null diff-list)
  1070.       (insert "\n\t***  No differences  ***\n"))
  1071.  
  1072.       (while diff-list
  1073.     (setq file (car (car diff-list))
  1074.           code (cdr (car diff-list))
  1075.           diff-list (cdr diff-list))
  1076.     (if (= (mod code 2) 0) ; dir1
  1077.         (insert (format "%-27s"
  1078.                 (ediff-truncate-string-left
  1079.                  (ediff-abbreviate-file-name
  1080.                   (if (file-directory-p (concat dir1 file))
  1081.                   (file-name-as-directory file)
  1082.                 file))
  1083.                  24)))
  1084.       (insert (format "%-27s" "---")))
  1085.     (if (= (mod code 3) 0) ; dir2
  1086.         (insert (format "%-26s"
  1087.                 (ediff-truncate-string-left
  1088.                  (ediff-abbreviate-file-name
  1089.                   (if (file-directory-p (concat dir2 file))
  1090.                   (file-name-as-directory file)
  1091.                 file))
  1092.                  24)))
  1093.       (insert (format "%-26s" "---")))
  1094.     (if (stringp dir3)
  1095.         (if (= (mod code 5) 0) ; dir3
  1096.         (insert (format " %-25s" 
  1097.                 (ediff-truncate-string-left
  1098.                  (ediff-abbreviate-file-name
  1099.                   (if (file-directory-p (concat dir3 file))
  1100.                       (file-name-as-directory file)
  1101.                     file))
  1102.                  24)))
  1103.           (insert (format " %-25s" "---"))))
  1104.     (insert "\n"))
  1105.       (setq buffer-read-only t)
  1106.       (set-buffer-modified-p nil)) ; eval in diff buffer
  1107.   ))
  1108.  
  1109. (defun ediff-bury-dir-diffs-buffer ()
  1110.   "Bury the directory difference buffer. Display the meta buffer instead."
  1111.   (interactive)
  1112.   (let ((buf ediff-meta-buffer)
  1113.     wind)
  1114.     (bury-buffer)
  1115.     (if (setq wind (ediff-get-visible-buffer-window buf))
  1116.     (select-window wind)
  1117.       (set-window-buffer (selected-window) buf))))
  1118.  
  1119. ;; executes in dir session group buffer
  1120. ;; show buffer differences
  1121. (defun ediff-show-dir-diffs ()
  1122.   "Display differences among the directories involved in session group."
  1123.   (interactive)
  1124.   (if (ediff-one-filegroup-metajob)
  1125.       (error "This command is inapplicable in the present context"))
  1126.   (or (ediff-buffer-live-p ediff-dir-diffs-buffer)
  1127.       (ediff-draw-dir-diffs ediff-dir-difference-list))
  1128.   (let ((buf ediff-dir-diffs-buffer))
  1129.     (other-window 1)
  1130.     (set-window-buffer (selected-window) buf)
  1131.     (goto-char (point-min))))
  1132.  
  1133. (defun ediff-up-meta-hierarchy ()
  1134.   "Go to the parent session group buffer."
  1135.   (interactive)
  1136.   (if (ediff-buffer-live-p ediff-parent-meta-buffer)
  1137.       (ediff-show-meta-buffer
  1138.        ediff-parent-meta-buffer ediff-meta-session-number)
  1139.     (error "This session group has no parent")))
  1140.   
  1141.  
  1142. ;; argument is ignored
  1143. (defun ediff-redraw-registry-buffer (&optional ignore)
  1144.   (ediff-with-current-buffer ediff-registry-buffer
  1145.     (let ((point (point))
  1146.       elt bufAname bufBname bufCname cur-diff total-diffs pt
  1147.       job-name meta-list registry-list buffer-read-only)
  1148.       (erase-buffer)
  1149.       ;; delete phony overlays that used to represent sessions before the buff
  1150.       ;; was redrawn
  1151.       (if ediff-emacs-p
  1152.       (mapcar 'delete-overlay (overlays-in 1 1))
  1153.     (map-extents 'delete-extent))
  1154.  
  1155.       (insert "This is a registry of all active Ediff sessions.
  1156.  
  1157. Useful commands:
  1158.      button2, `v', RET over a session record:  switch to that session
  1159.      M over a session record:  display the associated session group
  1160.      R in any Ediff session:   display session registry
  1161.      n,SPC: next session
  1162.      p,DEL: previous session
  1163.          E: browse Ediff on-line manual
  1164.          q: bury registry
  1165.  
  1166.  
  1167. \t\tActive Ediff Sessions:
  1168. \t\t----------------------
  1169.  
  1170. ")
  1171.       ;; purge registry list from dead buffers
  1172.       (mapcar (function (lambda (elt)
  1173.               (if (not (ediff-buffer-live-p elt))
  1174.                   (setq ediff-session-registry
  1175.                     (delq elt ediff-session-registry)))))
  1176.           ediff-session-registry)
  1177.  
  1178.       (if (null ediff-session-registry)
  1179.       (insert "       ******* No active Ediff sessions *******\n"))
  1180.  
  1181.       (setq registry-list ediff-session-registry)
  1182.       (while registry-list
  1183.     (setq elt (car registry-list)
  1184.           registry-list (cdr registry-list))
  1185.     
  1186.     (if (ediff-buffer-live-p elt)
  1187.         (if (ediff-with-current-buffer elt
  1188.           (setq job-name ediff-metajob-name
  1189.             meta-list ediff-meta-list)
  1190.           (and ediff-metajob-name
  1191.                (not (eq ediff-metajob-name 'ediff-registry))))
  1192.         (progn
  1193.           (setq pt (point))
  1194.           (insert (format "  *group*\t%s: %s\n"
  1195.                   (buffer-name elt)
  1196.                   (ediff-abbrev-jobname job-name)))
  1197.           (insert (format "\t\t   %s   %s   %s\n"
  1198.                   (ediff-abbreviate-file-name
  1199.                    (ediff-get-group-objA meta-list))
  1200.                   (ediff-abbreviate-file-name
  1201.                    (if (stringp
  1202.                     (ediff-get-group-objB meta-list))
  1203.                        (ediff-get-group-objB meta-list)
  1204.                        ""))
  1205.                   (ediff-abbreviate-file-name
  1206.                    (if (stringp
  1207.                     (ediff-get-group-objC meta-list))
  1208.                        (ediff-get-group-objC meta-list)
  1209.                        ""))))
  1210.           (ediff-set-meta-overlay pt (point) elt))
  1211.           (progn
  1212.         (ediff-with-current-buffer elt
  1213.           (setq bufAname (if (ediff-buffer-live-p ediff-buffer-A)
  1214.                      (buffer-name ediff-buffer-A)
  1215.                    "!!!killed buffer!!!")
  1216.             bufBname (if (ediff-buffer-live-p ediff-buffer-B)
  1217.                      (buffer-name ediff-buffer-B)
  1218.                    "!!!killed buffer!!!")
  1219.             bufCname (cond ((not (ediff-3way-job))
  1220.                     "")
  1221.                        ((ediff-buffer-live-p ediff-buffer-C)
  1222.                     (buffer-name ediff-buffer-C))
  1223.                        (t "!!!killed buffer!!!")))
  1224.           (setq total-diffs (format "%-4d" ediff-number-of-differences)
  1225.             cur-diff
  1226.             (cond ((= ediff-current-difference -1) "   _")
  1227.                   ((= ediff-current-difference
  1228.                   ediff-number-of-differences)
  1229.                    "   $")
  1230.                   (t (format
  1231.                   "%4d" (1+ ediff-current-difference))))
  1232.             job-name ediff-job-name))
  1233.         ;; back in the meta buf
  1234.         (setq pt (point))
  1235.         (insert cur-diff "/" total-diffs "\t"
  1236.             (buffer-name elt)
  1237.             (format ": %s"     (ediff-abbrev-jobname job-name)))
  1238.         (insert
  1239.          "\n\t\t   " bufAname "   " bufBname "   " bufCname "\n")
  1240.         (ediff-set-meta-overlay pt (point) elt))))
  1241.     ) ; while
  1242.       (set-buffer-modified-p nil)
  1243.       (goto-char point)
  1244.       )))
  1245.  
  1246. ;; Sets overlay around a meta record with 'ediff-meta-info property PROP
  1247. ;; If optional SESSION-NUMBER, make it a property of the overlay,
  1248. ;; ediff-meta-session-number
  1249. (defun ediff-set-meta-overlay (b e prop &optional session-number hidden)
  1250.   (let (overl)
  1251.     (setq overl (ediff-make-overlay b e))
  1252.     (if ediff-emacs-p
  1253.     (ediff-overlay-put overl 'mouse-face 'highlight)
  1254.       (ediff-overlay-put overl 'highlight t))
  1255.     (ediff-overlay-put overl 'ediff-meta-info prop)
  1256.     (ediff-overlay-put overl 'invisible hidden)
  1257.     (if (numberp session-number)
  1258.     (ediff-overlay-put overl 'ediff-meta-session-number session-number))))
  1259.  
  1260. (defun ediff-mark-for-hiding (unmark)
  1261.   "Mark session for hiding. With prefix arg, unmark."
  1262.   (interactive "P")
  1263.   (let* ((pos (ediff-event-point last-command-event))
  1264.      (meta-buf (ediff-event-buffer last-command-event))
  1265.      ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
  1266.      (info (ediff-get-meta-info meta-buf pos))
  1267.      (session-buf (ediff-get-session-buffer info))
  1268.      (session-number (ediff-get-session-number-at-pos pos)))
  1269.     (if (eq (ediff-get-session-status info) ?H)
  1270.     (setq unmark t))
  1271.     (if unmark
  1272.     (ediff-set-session-status info nil)
  1273.       (if (ediff-buffer-live-p session-buf)
  1274.       (error "Can't hide active session, %s" (buffer-name session-buf)))
  1275.       (ediff-set-session-status info ?H))
  1276.     (or unmark
  1277.     (ediff-next-meta-item 1))
  1278.     (ediff-update-meta-buffer meta-buf nil session-number)
  1279.     ))
  1280.  
  1281. (defun ediff-mark-for-operation (unmark)
  1282.   "Mark session for a group operation. With prefix arg, unmark."
  1283.   (interactive "P")
  1284.   (let* ((pos (ediff-event-point last-command-event))
  1285.      (meta-buf (ediff-event-buffer last-command-event))
  1286.      ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
  1287.      (info (ediff-get-meta-info meta-buf pos))
  1288.      (session-number (ediff-get-session-number-at-pos pos)))
  1289.     (if (eq (ediff-get-session-status info) ?*)
  1290.     (setq unmark t))
  1291.     (if unmark
  1292.     (ediff-set-session-status info nil)
  1293.       (ediff-set-session-status info ?*))
  1294.     (or unmark
  1295.     (ediff-next-meta-item 1))
  1296.     (ediff-update-meta-buffer meta-buf nil session-number)
  1297.     ))
  1298.  
  1299. (defun ediff-hide-marked-sessions (unhide)
  1300.   "Hide marked sessions. With prefix arg, unhide."
  1301.   (interactive "P")
  1302.   (let ((grp-buf (ediff-get-group-buffer ediff-meta-list))
  1303.     (meta-list (cdr ediff-meta-list))
  1304.     (from (if unhide ?I ?H))
  1305.     (to (if unhide ?H ?I))
  1306.     (numMarked 0)
  1307.     active-sessions-exist session-buf elt)
  1308.     (while meta-list
  1309.       (setq elt (car meta-list)
  1310.         meta-list (cdr meta-list)
  1311.         session-buf (ediff-get-session-buffer elt))
  1312.  
  1313.       (if (eq (ediff-get-session-status elt) from)
  1314.       (progn
  1315.         (setq numMarked (1+ numMarked))
  1316.         (if (and (eq to ?I) (buffer-live-p session-buf))
  1317.         ;; shouldn't hide active sessions
  1318.         (setq active-sessions-exist t)
  1319.           (ediff-set-session-status elt to)))))
  1320.     (if (> numMarked 0)
  1321.     (ediff-update-meta-buffer grp-buf)
  1322.       (beep)
  1323.       (if unhide
  1324.       (message "Nothing to reveal...")
  1325.     (message "Nothing to hide...")))
  1326.     (if active-sessions-exist
  1327.     (message "Note: Ediff didn't hide active sessions!"))
  1328.     ))
  1329.  
  1330. ;; Apply OPERATION to marked sessions. Operation expects one argument of type
  1331. ;; meta-list member (not the first one), i.e., a regular session description.
  1332. ;; Returns number of marked sessions on which operation was performed
  1333. (defun ediff-operate-on-marked-sessions (operation)
  1334.   (let ((grp-buf (ediff-get-group-buffer ediff-meta-list))
  1335.     (meta-list (cdr ediff-meta-list))
  1336.     (marksym ?*)
  1337.     (numMarked 0)
  1338.     (sessionNum 0)
  1339.     (diff-buffer ediff-meta-diff-buffer)
  1340.     session-buf elt)
  1341.     (while meta-list
  1342.       (setq elt (car meta-list)
  1343.         meta-list (cdr meta-list)
  1344.         sessionNum (1+ sessionNum))
  1345.       (cond ((eq (ediff-get-session-status elt) marksym)
  1346.          (save-excursion
  1347.            (setq numMarked (1+ numMarked))
  1348.            (funcall operation elt sessionNum)))
  1349.         ((and  (ediff-meta-session-p elt) 
  1350.            (ediff-buffer-live-p 
  1351.             (setq session-buf (ediff-get-session-buffer elt))))
  1352.          (setq numMarked
  1353.            (+ numMarked 
  1354.               (ediff-with-current-buffer session-buf
  1355.             ;; pass meta-diff along
  1356.             (setq ediff-meta-diff-buffer diff-buffer)
  1357.             ;; collect diffs in child group
  1358.             (ediff-operate-on-marked-sessions operation)))))))
  1359.     (ediff-update-meta-buffer grp-buf 'must-redraw) ; just in case
  1360.     numMarked
  1361.     ))
  1362.  
  1363. (defun ediff-append-custom-diff (session sessionNum)
  1364.   (or (ediff-collect-diffs-metajob)
  1365.       (error "Hmm, I'd hate to do it to you ..."))
  1366.   (let ((session-buf (ediff-get-session-buffer session))
  1367.     (meta-diff-buff ediff-meta-diff-buffer)
  1368.     (metajob ediff-metajob-name)
  1369.     tmp-buf custom-diff-buf)
  1370.     (if (ediff-buffer-live-p session-buf)
  1371.     (ediff-with-current-buffer session-buf
  1372.       (if (eq ediff-control-buffer session-buf) ; individual session
  1373.           (progn
  1374.         (ediff-compute-custom-diffs-maybe)
  1375.         (setq custom-diff-buf ediff-custom-diff-buffer)))))
  1376.  
  1377.     (or (ediff-buffer-live-p meta-diff-buff)
  1378.     (error "Ediff: something wrong--no multiple diffs buffer"))
  1379.  
  1380.     (cond ((ediff-buffer-live-p custom-diff-buf)
  1381.        (save-excursion
  1382.          (set-buffer meta-diff-buff)
  1383.          (goto-char (point-max))
  1384.          (insert-buffer custom-diff-buf)
  1385.          (insert "\n")))
  1386.       ((memq metajob '(ediff-directories 
  1387.                ediff-merge-directories
  1388.                ediff-merge-directories-with-ancestor))
  1389.        ;; get diffs by calling shell command on ediff-custom-diff-program
  1390.        (save-excursion
  1391.          (set-buffer (setq tmp-buf (get-buffer-create ediff-tmp-buffer)))
  1392.          (erase-buffer)
  1393.          (shell-command
  1394.           (format "%s %s %s %s"
  1395.               ediff-custom-diff-program ediff-custom-diff-options
  1396.               (ediff-get-session-objA-name session)
  1397.               (ediff-get-session-objB-name session))
  1398.           t))
  1399.        (save-excursion
  1400.          (set-buffer meta-diff-buff)
  1401.          (goto-char (point-max))
  1402.          (insert-buffer tmp-buf)
  1403.          (insert "\n")))
  1404.       (t
  1405.        (error "Can't make context diff for Session %d" sessionNum )))
  1406.     ))
  1407.  
  1408. (defun ediff-collect-custom-diffs ()
  1409.   "Collect custom diffs of marked sessions in buffer `*Ediff Multifile Diffs*'.
  1410. This operation is defined only for `ediff-directories' and
  1411. `ediff-directory-revisions', since its intent is to produce
  1412. multifile patches. For `ediff-directory-revisions', we insist that
  1413. all marked sessions must be active."
  1414.   (interactive)
  1415.   (or (ediff-buffer-live-p ediff-meta-diff-buffer)
  1416.       (setq ediff-meta-diff-buffer
  1417.         (get-buffer-create
  1418.          (ediff-unique-buffer-name "*Ediff Multifile Diffs" "*"))))
  1419.   (ediff-with-current-buffer ediff-meta-diff-buffer
  1420.     (erase-buffer))
  1421.   (if (> (ediff-operate-on-marked-sessions 'ediff-append-custom-diff) 0)
  1422.       ;; did something
  1423.       (display-buffer ediff-meta-diff-buffer 'not-this-window)
  1424.     (beep)
  1425.     (message "No marked sessions found")))
  1426.  
  1427. (defun ediff-meta-show-patch ()
  1428.   "Show the multi-file patch associated with this group session."
  1429.   (interactive)
  1430.   (let* ((pos (ediff-event-point last-command-event))
  1431.      (meta-buf (ediff-event-buffer last-command-event))
  1432.      (info (ediff-get-meta-info meta-buf pos 'noerror))
  1433.      (patchbuffer ediff-meta-patchbufer))
  1434.     (if (ediff-buffer-live-p patchbuffer)
  1435.     (ediff-with-current-buffer patchbuffer
  1436.       (save-restriction
  1437.         (if (not info)
  1438.         (widen)
  1439.           (narrow-to-region
  1440.            (ediff-get-session-objB-name info)
  1441.            (ediff-get-session-objC-name info)))
  1442.         (set-buffer (get-buffer-create ediff-tmp-buffer))
  1443.         (erase-buffer)
  1444.         (insert-buffer patchbuffer)
  1445.         (display-buffer ediff-tmp-buffer 'not-this-window)
  1446.         ))
  1447.       (error "The patch buffer wasn't found"))))
  1448.  
  1449.           
  1450. ;; This function executes in meta buffer. It knows where event happened.
  1451. (defun ediff-filegroup-action ()
  1452.   "Execute appropriate action for the selected session."
  1453.   (interactive)
  1454.   (let* ((pos (ediff-event-point last-command-event))
  1455.      (meta-buf (ediff-event-buffer last-command-event))
  1456.      ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
  1457.      (info (ediff-get-meta-info meta-buf pos))
  1458.      (session-buf (ediff-get-session-buffer info))
  1459.      (session-number (ediff-get-session-number-at-pos pos meta-buf))
  1460.      merge-autostore-dir file1 file2 file3 regexp)
  1461.  
  1462.     (setq file1 (ediff-get-session-objA-name info)
  1463.       file2 (ediff-get-session-objB-name info)
  1464.       file3 (ediff-get-session-objC-name info))
  1465.  
  1466.     ;; make sure we don't start on hidden sessions
  1467.     ;; ?H means marked for hiding. ?I means invalid (hidden).
  1468.     (if (memq (ediff-get-session-status info) '(?I))
  1469.     (progn
  1470.       (beep)
  1471.       (if (y-or-n-p "This session is marked as hidden, unmark? ")
  1472.           (progn
  1473.         (ediff-set-session-status info nil)
  1474.         (ediff-update-meta-buffer meta-buf nil session-number))
  1475.         (error "Aborted"))))
  1476.  
  1477.     (ediff-with-current-buffer meta-buf
  1478.       (setq merge-autostore-dir
  1479.         (ediff-get-group-merge-autostore-dir ediff-meta-list))
  1480.       (goto-char pos) ; if the user clicked on session--move point there
  1481.       ;; First handle sessions involving directories (which are themselves
  1482.       ;; session groups)
  1483.       ;; After that handle individual sessions
  1484.       (cond ((ediff-meta-session-p info)
  1485.          ;; do ediff/ediff-merge on subdirectories
  1486.          (if (ediff-buffer-live-p session-buf)
  1487.          (ediff-show-meta-buffer session-buf)
  1488.            (setq regexp (read-string "Filter through regular expression: " 
  1489.                      nil 'ediff-filtering-regexp-history))
  1490.            (ediff-directories-internal
  1491.         file1 file2 file3 regexp
  1492.         ediff-session-action-function
  1493.         ediff-metajob-name 
  1494.         ;; make it update car info after startup
  1495.         (` (list (lambda () 
  1496.                ;; child session group should know its parent
  1497.                (setq ediff-parent-meta-buffer
  1498.                  (quote (, ediff-meta-buffer))
  1499.                  ediff-meta-session-number
  1500.                  (, session-number))
  1501.                ;; and parent will know its child
  1502.                (setcar (quote (, info)) ediff-meta-buffer)))))))
  1503.  
  1504.         ;; Do ediff-revision on a subdirectory
  1505.         ((and (ediff-one-filegroup-metajob)
  1506.           (ediff-revision-metajob)
  1507.           (file-directory-p file1))
  1508.          (if (ediff-buffer-live-p session-buf)
  1509.          (ediff-show-meta-buffer session-buf)
  1510.            (setq regexp (read-string "Filter through regular expression: " 
  1511.                      nil 'ediff-filtering-regexp-history))
  1512.            (ediff-directory-revisions-internal
  1513.         file1 regexp
  1514.         ediff-session-action-function ediff-metajob-name
  1515.         ;; make it update car info after startup
  1516.         (` (list (lambda () 
  1517.                ;; child session group should know its parent and
  1518.                ;; its number
  1519.                (setq ediff-parent-meta-buffer
  1520.                  (quote (, ediff-meta-buffer))
  1521.                  ediff-meta-session-number
  1522.                  (, session-number))
  1523.                ;; and parent will know its child
  1524.                (setcar (quote (, info)) ediff-meta-buffer)))))))
  1525.  
  1526.         ;; From here on---only individual session handlers
  1527.  
  1528.         ;; handle an individual session with a live control buffer
  1529.         ((ediff-buffer-live-p session-buf)
  1530.          (ediff-with-current-buffer session-buf
  1531.            (setq ediff-mouse-pixel-position (mouse-pixel-position))
  1532.            (ediff-recenter 'no-rehighlight)))
  1533.  
  1534.         ((ediff-problematic-session-p info)
  1535.          (beep)
  1536.          (if (y-or-n-p
  1537.           "This session has no ancestor. Merge without the ancestor? ")
  1538.          (ediff-merge-files
  1539.           file1 file2
  1540.           ;; provide startup hooks 
  1541.           (` (list (lambda () 
  1542.                  (setq ediff-meta-buffer (, (current-buffer))
  1543.                    ediff-meta-session-number
  1544.                    (, session-number))
  1545.                  (setq ediff-merge-store-file
  1546.                    (, (concat
  1547.                        merge-autostore-dir
  1548.                        "merge_"
  1549.                        (file-name-nondirectory file1))))
  1550.                  ;; make ediff-startup pass
  1551.                  ;; ediff-control-buffer back to the meta
  1552.                  ;; level; see below
  1553.                  (setcar
  1554.                   (quote (, info)) ediff-control-buffer)))))
  1555.            (error "Aborted")))
  1556.         ((ediff-one-filegroup-metajob)     ; needs 1 file arg
  1557.          (funcall ediff-session-action-function
  1558.               file1
  1559.               ;; provide startup hooks 
  1560.               (` (list (lambda () 
  1561.                  (setq ediff-meta-buffer (, (current-buffer))
  1562.                        ediff-meta-session-number
  1563.                        (, session-number))
  1564.                  (setq ediff-merge-store-file
  1565.                        (, (concat
  1566.                        merge-autostore-dir
  1567.                        "merge_"
  1568.                        (file-name-nondirectory file1))))
  1569.                  ;; make ediff-startup pass
  1570.                  ;; ediff-control-buffer back to the meta
  1571.                  ;; level; see below
  1572.                  (setcar
  1573.                   (quote (, info)) ediff-control-buffer))))))
  1574.         ((not (ediff-metajob3))      ; need 2 file args
  1575.          (funcall ediff-session-action-function
  1576.               file1 file2
  1577.               ;; provide startup hooks 
  1578.               (` (list (lambda () 
  1579.                  (setq ediff-meta-buffer (, (current-buffer))
  1580.                        ediff-meta-session-number
  1581.                        (, session-number))
  1582.                  (setq ediff-merge-store-file
  1583.                        (, (concat
  1584.                        merge-autostore-dir
  1585.                        "merge_"
  1586.                        (file-name-nondirectory file1))))
  1587.                  ;; make ediff-startup pass
  1588.                  ;; ediff-control-buffer back to the meta
  1589.                  ;; level; see below
  1590.                  (setcar
  1591.                   (quote (, info)) ediff-control-buffer))))))
  1592.         ((ediff-metajob3)      ; need 3 file args
  1593.          (funcall ediff-session-action-function
  1594.               file1 file2 file3
  1595.               ;; arrange startup hooks 
  1596.               (` (list (lambda () 
  1597.                  (setq ediff-merge-store-file
  1598.                        (, (concat
  1599.                        merge-autostore-dir
  1600.                        "merge_"
  1601.                        (file-name-nondirectory file1))))
  1602.                  (setq ediff-meta-buffer (, (current-buffer))
  1603.                        ediff-meta-session-number
  1604.                        (, session-number))
  1605.                  ;; this arranges that ediff-startup will pass
  1606.                  ;; the value of ediff-control-buffer back to
  1607.                  ;; the meta level, to the record in the meta
  1608.                  ;; list containing the information about the
  1609.                  ;; session associated with that
  1610.                  ;; ediff-control-buffer
  1611.                  (setcar
  1612.                   (quote (, info)) ediff-control-buffer))))))
  1613.         ) ; cond
  1614.       ) ; eval in meta-buf
  1615.     ))
  1616.  
  1617. (defun ediff-registry-action ()
  1618.   "Switch to a selected session."
  1619.   (interactive)
  1620.   (let* ((pos (ediff-event-point last-command-event))
  1621.      (buf (ediff-event-buffer last-command-event))
  1622.      (ctl-buf (ediff-get-meta-info buf pos)))
  1623.  
  1624.     (if (ediff-buffer-live-p ctl-buf)
  1625.     ;; check if this is ediff-control-buffer or ediff-meta-buffer
  1626.     (if (ediff-with-current-buffer ctl-buf
  1627.           (eq (key-binding "q") 'ediff-quit-meta-buffer))
  1628.         ;; it's a meta-buffer -- last action should just display it
  1629.         (ediff-show-meta-buffer ctl-buf t)
  1630.       ;; it's a session buffer -- invoke go back to session
  1631.       (ediff-with-current-buffer ctl-buf
  1632.         (setq ediff-mouse-pixel-position (mouse-pixel-position))
  1633.         (ediff-recenter 'no-rehighlight)))
  1634.       (beep)
  1635.       (message "You've selected a stale session --- try again")
  1636.       (ediff-update-registry))
  1637.     (ediff-with-current-buffer buf
  1638.       (goto-char pos))
  1639.     ))
  1640.  
  1641.  
  1642. ;; If session number is t, means don't update meta buffer
  1643. (defun ediff-show-meta-buffer (&optional meta-buf session-number)
  1644.   "Show the session group buffer."
  1645.   (interactive)
  1646.   (let (wind frame silent)
  1647.     (if meta-buf (setq silent t))
  1648.  
  1649.     (setq meta-buf (or meta-buf ediff-meta-buffer))
  1650.     (cond ((not (bufferp meta-buf))
  1651.        (error "This Ediff session is not part of a session group"))
  1652.       ((not (ediff-buffer-live-p meta-buf))
  1653.        (error
  1654.         "Can't find this session's group panel -- session itself is ok")))
  1655.  
  1656.     (cond ((numberp session-number)
  1657.        (ediff-update-meta-buffer meta-buf nil session-number))
  1658.       ;; if session-number is t, don't update
  1659.       (session-number)
  1660.       (t (ediff-cleanup-meta-buffer meta-buf)))
  1661.  
  1662.     (ediff-with-current-buffer meta-buf
  1663.       (save-excursion
  1664.     (cond ((setq wind (ediff-get-visible-buffer-window meta-buf))
  1665.            (or silent
  1666.            (message
  1667.             "Already showing the group panel for this session"))
  1668.            (set-window-buffer wind meta-buf)
  1669.            (select-window wind))
  1670.           ((window-live-p (setq wind ediff-window-C)) ;in merge--merge buf
  1671.            (set-window-buffer ediff-window-C meta-buf)
  1672.            (select-window wind))
  1673.           ((window-live-p (setq wind ediff-window-A))
  1674.            (set-window-buffer ediff-window-A meta-buf)
  1675.            (select-window wind))
  1676.           ((window-live-p (setq wind ediff-window-B))
  1677.            (set-window-buffer ediff-window-B meta-buf)
  1678.            (select-window wind))
  1679.           ((and
  1680.         (setq wind
  1681.               (ediff-get-visible-buffer-window ediff-registry-buffer))
  1682.         (ediff-window-display-p))
  1683.            (select-window wind)
  1684.            (other-window 1)
  1685.            (set-window-buffer (selected-window) meta-buf))
  1686.           (t (ediff-skip-unsuitable-frames 'ok-unsplittable)
  1687.          (set-window-buffer (selected-window) meta-buf)))
  1688.     ))
  1689.     (if (and (ediff-window-display-p)
  1690.          (window-live-p 
  1691.           (setq wind (ediff-get-visible-buffer-window meta-buf))))
  1692.     (progn
  1693.       (setq frame (window-frame wind))
  1694.       (raise-frame frame)
  1695.       (ediff-reset-mouse frame)))
  1696.     (run-hooks 'ediff-show-session-group-hook)
  1697.     ))
  1698.  
  1699. (defun ediff-show-current-session-meta-buffer ()
  1700.   (interactive)
  1701.   (ediff-show-meta-buffer nil ediff-meta-session-number))
  1702.  
  1703. (defun ediff-show-meta-buff-from-registry ()
  1704.   "Display the session group buffer for a selected session group."
  1705.   (interactive)
  1706.   (let* ((pos (ediff-event-point last-command-event))
  1707.      (meta-buf (ediff-event-buffer last-command-event))
  1708.      (info (ediff-get-meta-info meta-buf pos))
  1709.      (meta-or-session-buf info))
  1710.     (ediff-with-current-buffer meta-or-session-buf
  1711.       (ediff-show-meta-buffer nil t))))
  1712.  
  1713. ;;;###autoload
  1714. (defun ediff-show-registry ()
  1715.   "Display Ediff's registry."
  1716.   (interactive)
  1717.   (ediff-update-registry)
  1718.   (if (not (ediff-buffer-live-p ediff-registry-buffer))
  1719.       (error "No active Ediff sessions or corrupted session registry"))
  1720.   (let (wind frame)
  1721.     ;; for some reason, point moves in ediff-registry-buffer, so we preserve it
  1722.     ;; explicitly
  1723.     (ediff-with-current-buffer ediff-registry-buffer
  1724.       (save-excursion
  1725.     (cond  ((setq wind
  1726.               (ediff-get-visible-buffer-window ediff-registry-buffer))
  1727.         (message "Already showing the registry")
  1728.         (set-window-buffer wind ediff-registry-buffer)
  1729.         (select-window wind))
  1730.            ((window-live-p ediff-window-C)
  1731.         (set-window-buffer ediff-window-C ediff-registry-buffer)
  1732.         (select-window ediff-window-C))
  1733.            ((window-live-p ediff-window-A)
  1734.         (set-window-buffer ediff-window-A ediff-registry-buffer)
  1735.         (select-window ediff-window-A))
  1736.            ((window-live-p ediff-window-B)
  1737.         (set-window-buffer ediff-window-B ediff-registry-buffer)
  1738.         (select-window ediff-window-B))
  1739.            ((and (setq wind
  1740.                (ediff-get-visible-buffer-window ediff-meta-buffer))
  1741.              (ediff-window-display-p))
  1742.         (select-window wind)
  1743.         (other-window 1)
  1744.         (set-window-buffer (selected-window) ediff-registry-buffer))
  1745.            (t (ediff-skip-unsuitable-frames 'ok-unsplittable)
  1746.           (set-window-buffer (selected-window) ediff-registry-buffer)))
  1747.     ))
  1748.     (if (ediff-window-display-p)
  1749.     (progn
  1750.       (setq frame 
  1751.         (window-frame
  1752.          (ediff-get-visible-buffer-window ediff-registry-buffer)))
  1753.       (raise-frame frame)
  1754.       (ediff-reset-mouse frame)))
  1755.     (run-hooks 'ediff-show-registry-hook)
  1756.     ))
  1757.  
  1758. ;;;###autoload
  1759. (defalias 'eregistry 'ediff-show-registry)
  1760.  
  1761. ;; If meta-buf doesn't exist, it is created. In that case, id doesn't have a
  1762. ;; parent meta-buf
  1763. ;; Check if META-BUF exists before calling this function
  1764. ;; Optional MUST-REDRAW, if non-nil, would force redrawal of the whole meta
  1765. ;; buffer. Otherwise, it will just go over the buffer and update activity marks
  1766. ;; and session status.
  1767. ;; SESSION-NUMBER, if specified, says which session caused the update.
  1768. (defun ediff-update-meta-buffer (meta-buf &optional must-redraw session-number)
  1769.   (if (ediff-buffer-live-p meta-buf)
  1770.       (ediff-with-current-buffer meta-buf
  1771.     (cond (must-redraw ; completely redraw the meta buffer
  1772.            (funcall ediff-meta-redraw-function ediff-meta-list))
  1773.           ((numberp session-number) ; redraw only for the given session
  1774.            (ediff-update-session-marker-in-dir-meta-buffer session-number))
  1775.           (t ; update only what's changed, but scan the entire meta buffer
  1776.            (ediff-update-markers-in-dir-meta-buffer ediff-meta-list)))
  1777.     )))
  1778.  
  1779. (defun ediff-update-registry ()
  1780.   (ediff-with-current-buffer (current-buffer)
  1781.     (if (ediff-buffer-live-p ediff-registry-buffer)
  1782.     (ediff-redraw-registry-buffer)
  1783.       (ediff-prepare-meta-buffer 
  1784.        'ediff-registry-action
  1785.        ediff-session-registry
  1786.        "*Ediff Registry"
  1787.        'ediff-redraw-registry-buffer
  1788.        'ediff-registry))
  1789.     ))
  1790.  
  1791. ;; If meta-buf exists, it is redrawn along with parent. 
  1792. ;; Otherwise, nothing happens.
  1793. (defun ediff-cleanup-meta-buffer (meta-buffer)
  1794.   (if (ediff-buffer-live-p meta-buffer)
  1795.       (ediff-with-current-buffer meta-buffer
  1796.     (ediff-update-meta-buffer meta-buffer)
  1797.     (if (ediff-buffer-live-p ediff-parent-meta-buffer)
  1798.         (ediff-update-meta-buffer
  1799.          ediff-parent-meta-buffer nil ediff-meta-session-number)))))
  1800.  
  1801. ;; t if no session is in progress
  1802. (defun ediff-safe-to-quit (meta-buffer)
  1803.   (if (ediff-buffer-live-p meta-buffer)
  1804.       (let ((lis ediff-meta-list)
  1805.         (cont t)
  1806.         buffer-read-only)
  1807.     ;;(ediff-update-meta-buffer meta-buffer)
  1808.     (ediff-with-current-buffer meta-buffer
  1809.       (setq lis (cdr lis)) ; discard the description part of meta-list
  1810.       (while (and cont lis)
  1811.         (if (ediff-buffer-live-p
  1812.          (ediff-get-group-buffer lis)) ; in progress
  1813.         (setq cont nil))
  1814.         (setq lis (cdr lis)))
  1815.       cont))))
  1816.  
  1817. (defun ediff-quit-meta-buffer ()
  1818.   "If the group has no active session, delete the meta buffer.
  1819. If no session is in progress, ask to confirm before deleting meta buffer.
  1820. Otherwise, bury the meta buffer.
  1821. If this is a session registry buffer then just bury it."
  1822.   (interactive)
  1823.   (let* ((buf (current-buffer))
  1824.      (dir-diffs-buffer ediff-dir-diffs-buffer)
  1825.      (meta-diff-buffer ediff-meta-diff-buffer)
  1826.      (session-number ediff-meta-session-number)
  1827.      (parent-buf ediff-parent-meta-buffer)
  1828.      (dont-show-registry (eq buf ediff-registry-buffer)))
  1829.     (if dont-show-registry
  1830.     (bury-buffer)
  1831.       ;;(ediff-cleanup-meta-buffer buf)
  1832.       (cond ((and (ediff-safe-to-quit buf)
  1833.           (y-or-n-p "Quit this session group? "))
  1834.          (run-hooks 'ediff-quit-session-group-hook)
  1835.          (message "")
  1836.          (ediff-dispose-of-meta-buffer buf))
  1837.         ((ediff-safe-to-quit buf)
  1838.          (bury-buffer))
  1839.         (t
  1840.          (error
  1841.           "This session group has active sessions---cannot exit")))
  1842.       (ediff-update-meta-buffer parent-buf nil session-number)
  1843.       (ediff-kill-buffer-carefully dir-diffs-buffer)
  1844.       (ediff-kill-buffer-carefully meta-diff-buffer)
  1845.       (if (ediff-buffer-live-p parent-buf)
  1846.       (progn
  1847.         (setq dont-show-registry t)
  1848.         (ediff-show-meta-buffer parent-buf session-number)))
  1849.       )
  1850.     (or dont-show-registry
  1851.     (ediff-show-registry))))
  1852.  
  1853. (defun ediff-dispose-of-meta-buffer (buf)
  1854.   (setq ediff-session-registry (delq buf ediff-session-registry))
  1855.   (ediff-with-current-buffer buf
  1856.     (if (ediff-buffer-live-p ediff-dir-diffs-buffer)
  1857.     (kill-buffer ediff-dir-diffs-buffer)))
  1858.   (kill-buffer buf))
  1859.     
  1860.  
  1861. ;; Obtain information on a meta record where the user clicked or typed
  1862. ;; BUF is the buffer where this happened and POINT is the position
  1863. ;; If optional NOERROR arg is given, don't report error and return nil if no
  1864. ;; meta info is found on line.
  1865. (defun ediff-get-meta-info (buf point &optional noerror)
  1866.   (let (result olist tmp)
  1867.     (if (and point (ediff-buffer-live-p buf))
  1868.     (ediff-with-current-buffer buf
  1869.       (if ediff-xemacs-p
  1870.           (setq result
  1871.             (if (setq tmp (extent-at point buf 'ediff-meta-info))
  1872.             (ediff-overlay-get tmp 'ediff-meta-info)))
  1873.         (setq olist (overlays-at point))
  1874.         (setq olist
  1875.           (mapcar (function (lambda (elt)
  1876.                       (overlay-get elt 'ediff-meta-info)))
  1877.               olist))
  1878.         (while (and olist (null (car olist))
  1879.             (overlay-get (car olist) 'invisible))
  1880.           (setq olist (cdr olist)))
  1881.         (setq result (car olist)))))
  1882.     (if result
  1883.     result
  1884.       (if noerror
  1885.       nil
  1886.     (ediff-update-registry)
  1887.     (error "No session info in this line")))))
  1888.  
  1889.  
  1890. (defun ediff-get-meta-overlay-at-pos (point)
  1891.   (if ediff-xemacs-p
  1892.       (extent-at point (current-buffer) 'ediff-meta-info)
  1893.     (let* ((overl-list (overlays-at point))
  1894.        (overl (car overl-list)))
  1895.       (while (and overl (null (overlay-get overl 'ediff-meta-info)))
  1896.     (setq overl-list (cdr overl-list)
  1897.           overl (car overl-list)))
  1898.       overl)))
  1899.  
  1900. (defsubst ediff-get-session-number-at-pos (point &optional meta-buffer)
  1901.   (setq meta-buffer (if (ediff-buffer-live-p meta-buffer)
  1902.             meta-buffer
  1903.               (current-buffer)))
  1904.   (ediff-with-current-buffer meta-buffer
  1905.     (ediff-overlay-get
  1906.      (ediff-get-meta-overlay-at-pos point) 'ediff-meta-session-number)))
  1907.  
  1908.  
  1909. ;; Return location of the next meta overlay after point
  1910. (defun ediff-next-meta-overlay-start (point)
  1911.   (if (eobp)
  1912.       (goto-char (point-min))
  1913.     (let ((overl (ediff-get-meta-overlay-at-pos point)))
  1914.       (if ediff-xemacs-p
  1915.       (progn
  1916.         (if overl
  1917.         (setq overl (next-extent overl))
  1918.           (setq overl (next-extent (current-buffer))))
  1919.         (if overl
  1920.         (extent-start-position overl)
  1921.           (point-max)))
  1922.     (if overl
  1923.         ;; note: end of current overlay is the beginning of the next one
  1924.         (overlay-end overl)
  1925.       (next-overlay-change point))))
  1926.     ))
  1927.  
  1928.  
  1929. (defun ediff-previous-meta-overlay-start (point)
  1930.   (if (bobp)
  1931.       (goto-char (point-max))
  1932.     (let ((overl (ediff-get-meta-overlay-at-pos point)))
  1933.       (if ediff-xemacs-p
  1934.       (progn
  1935.         (if overl
  1936.         (setq overl (previous-extent overl))
  1937.           (setq overl (previous-extent (current-buffer))))
  1938.         (if overl
  1939.         (extent-start-position overl)
  1940.           (point-min)))
  1941.     (if overl (setq point (overlay-start overl)))
  1942.     ;; to get to the beginning of prev overlay
  1943.     (if (not (bobp))
  1944.         ;; trick to overcome an emacs bug--doesn't always find previous
  1945.         ;; overlay change correctly
  1946.         (setq point (1- point)))
  1947.     (setq point (previous-overlay-change point))
  1948.     ;; If we are not over an overlay after subtracting 1, it means we are
  1949.     ;; in the description area preceding session records. In this case,
  1950.     ;; goto the top of the registry buffer.
  1951.     (or (car (overlays-at point))
  1952.         (setq point (point-min)))
  1953.     point))))
  1954.  
  1955. ;; this is the action invoked when the user selects a patch from the meta
  1956. ;; buffer.
  1957. (defun ediff-patch-file-form-meta (file &optional startup-hooks)
  1958.   (let* ((pos (ediff-event-point last-command-event))
  1959.      (meta-buf (ediff-event-buffer last-command-event))
  1960.      ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
  1961.      (info (ediff-get-meta-info meta-buf pos))
  1962.      (meta-patchbuf ediff-meta-patchbufer)
  1963.      session-buf beg-marker end-marker)
  1964.  
  1965.     (if (or (file-directory-p file) (string-match "/dev/null" file))
  1966.     (error "`%s' is not an ordinary file" (file-name-as-directory file)))
  1967.     (setq session-buf (ediff-get-session-buffer info)
  1968.       beg-marker (ediff-get-session-objB-name info)
  1969.       end-marker (ediff-get-session-objC-name info))
  1970.  
  1971.     (or (ediff-buffer-live-p session-buf) ; either an active patch session
  1972.     (null session-buf)            ; or it is a virgin session
  1973.     (error
  1974.      "Patch has been already applied to this file--cannot be repeated!"))
  1975.  
  1976.     (ediff-with-current-buffer meta-patchbuf
  1977.       (save-restriction
  1978.     (widen)
  1979.     (narrow-to-region beg-marker end-marker)
  1980.     (ediff-patch-file-internal meta-patchbuf file startup-hooks)))))
  1981.  
  1982.  
  1983. (defun ediff-meta-mark-equal-files ()
  1984.   "Run though the session list and mark identical files.
  1985. This is used only for sessions that involve 2 or 3 files at the same time."
  1986.   (interactive)
  1987.   (let ((list (cdr ediff-meta-list))
  1988.     fileinfo1 fileinfo2 fileinfo3 elt)
  1989.     (while (setq elt (car list))
  1990.       (setq fileinfo1 (ediff-get-session-objA elt)
  1991.         fileinfo2 (ediff-get-session-objB elt)
  1992.         fileinfo3 (ediff-get-session-objC elt))
  1993.       (ediff-set-file-eqstatus fileinfo1 nil)
  1994.       (ediff-set-file-eqstatus fileinfo2 nil)
  1995.       (ediff-set-file-eqstatus fileinfo3 nil)
  1996.  
  1997.       (ediff-mark-if-equal fileinfo1 fileinfo2)
  1998.       (if (ediff-metajob3)
  1999.       (progn
  2000.         (ediff-mark-if-equal fileinfo1 fileinfo3)
  2001.         (ediff-mark-if-equal fileinfo2 fileinfo3)))
  2002.       (setq list (cdr list))))
  2003.   (ediff-update-meta-buffer (current-buffer) 'must-redraw))
  2004.  
  2005. ;; mark files 1 and 2 as equal, if they are.
  2006. (defun ediff-mark-if-equal (fileinfo1 fileinfo2)
  2007.   (let ((f1 (car fileinfo1))
  2008.     (f2 (car fileinfo2)))
  2009.     (or (file-directory-p f1)
  2010.     (file-directory-p f2)
  2011.     (if (ediff-same-file-contents f1 f2)
  2012.         (progn
  2013.           (ediff-set-file-eqstatus fileinfo1 t)
  2014.           (ediff-set-file-eqstatus fileinfo2 t))))))
  2015.  
  2016.  
  2017.  
  2018. ;;; Local Variables:
  2019. ;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
  2020. ;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
  2021. ;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
  2022. ;;; End:
  2023.  
  2024. ;;; ediff-mult.el ends here
  2025.